//
// Created by Langingsing on 2021/10/24.
//
// 二维数组
#ifndef PROJ_MAP_H
#define PROJ_MAP_H

#include <iostream>

using namespace std;

template<typename T>
class Map
{
public:
    /**
     * 构造函数
     * @param cols 列数
     * @param rows 行数
     * */
    Map(size_t cols, size_t rows);

    /// 复制另一个二维数组
    Map(Map<T> const &m);

    ~Map();

    /// 获取列数
    inline size_t cols() const
    {
        return _cols;
    }

    /// 获取行数
    inline size_t rows() const
    {
        return _rows;
    }

    /// 获取二维数组元素值
    inline T val(size_t colIdx, size_t rowIdx) const;

    /// 设置二维数组元素值
    inline T val(size_t colIdx, size_t rowIdx, T val);

    /// 置值
    inline void emplace(T value)
    {
        for (size_t r = 0; r < rows(); ++r)
            for (size_t c = 0; c < cols(); ++c)
                val(c, r, value);
    }

    /// 输入二维数组
    inline friend istream &operator>>(istream &in, Map &m)
    {
        for (size_t r = 0; r < m.rows(); ++r)
            for (size_t c = 0; c < m.cols(); ++c)
                in >> m._basePtr[m._colRowToIdx(c, r)];

        return in;
    }

    /// 输出二维数组
    inline friend ostream &operator<<(ostream &out, Map &m)
    {
        for (size_t r = 0; r < m.rows(); ++r) {
            for (size_t c = 0; c < m.cols(); ++c) {
                out << m.val(c, r) << ' ';
            }
            out << endl;
        }

        return out;
    }

private:
    T *_basePtr; // 一维数组
    size_t const _cols;
    size_t const _rows;

    inline size_t _size() const;

    inline T &operator[](size_t i);

    inline size_t _colRowToIdx(size_t col, size_t row) const;
};

template<typename T>
Map<T>::Map(size_t cols, size_t rows)
        : _cols(cols),
          _rows(rows)
{
    _basePtr = new T[_size()];
    if (!_basePtr) {
        cout << "FAIL TO ALLOCATE MEMORY in map" << endl;
        throw bad_alloc();
    }
}

template<typename T>
Map<T>::Map(Map<T> const &m)
        :_cols(m._cols),
         _rows(m._rows),
         _basePtr(new T[m._size()])
{
    for (size_t i = 0; i < m._size(); ++i)
        _basePtr[i] = m._basePtr[i]; // 深拷贝
}

template<typename T>
Map<T>::~Map()
{
    delete[] _basePtr;
}

template<typename T>
inline T Map<T>::val(size_t colIdx, size_t rowIdx) const
{
    return _basePtr[_colRowToIdx(colIdx, rowIdx)];
}

template<typename T>
inline T Map<T>::val(size_t colIdx, size_t rowIdx, T val)
{
    return _basePtr[_colRowToIdx(colIdx, rowIdx)] = val;
}

template<typename T>
inline size_t Map<T>::_size() const
{
    return _cols * _rows;
}

template<typename T>
inline T &Map<T>::operator[](size_t i)
{
    return _basePtr[i];
}

template<typename T>
inline size_t Map<T>::_colRowToIdx(size_t colIdx, size_t rowIdx) const
{
    return colIdx * _rows + rowIdx;
}

#endif //PROJ_MAP_H
